/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package me.zhengjie.modules.weChat.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import me.zhengjie.modules.security.config.SecurityProperties;
import me.zhengjie.modules.security.security.TokenProvider;
import me.zhengjie.modules.security.service.OnlineUserService;
import me.zhengjie.modules.system.domain.User;
import me.zhengjie.modules.weChat.Dao.WeChatDao;
import me.zhengjie.modules.weChat.config.SystemConfig;
import me.zhengjie.modules.weChat.domain.SysWechatUser;
import me.zhengjie.modules.weChat.domain.vo.WeChatVo;
import me.zhengjie.modules.weChat.service.WXService;
import me.zhengjie.modules.weChat.service.dto.UserWeChat;
import me.zhengjie.modules.weChat.utils.WechatUtil;
import me.zhengjie.utils.*;
import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.weChat.repository.SysWechatUserRepository;
import me.zhengjie.modules.weChat.service.SysWechatUserService;
import me.zhengjie.modules.weChat.service.dto.SysWechatUserDto;
import me.zhengjie.modules.weChat.service.dto.SysWechatUserQueryCriteria;
import me.zhengjie.modules.weChat.service.mapstruct.SysWechatUserMapper;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.util.Assert;

import java.sql.Timestamp;
import java.util.*;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * @website https://docs.auauz.net
 * @description /
 * @author 陈震洺
 * @date 2020-05-11
 **/
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class SysWechatUserServiceImpl implements SysWechatUserService {

    private final SysWechatUserRepository sysWechatUserRepository;
    private final SysWechatUserMapper sysWechatUserMapper;
    private final SystemConfig systemConfig;
    private final WeChatDao weChatDao;
    private final WXService wxService;
    private final SecurityProperties properties;
    private final TokenProvider tokenProvider;
    private final OnlineUserService onlineUserService;

    @Override
    public Map<String,Object> queryAll(SysWechatUserQueryCriteria criteria, Pageable pageable){
        Page<SysWechatUser> page = sysWechatUserRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
        return PageUtil.toPage(page.map(sysWechatUserMapper::toDto));
    }

    @Override
    public List<SysWechatUserDto> queryAll(SysWechatUserQueryCriteria criteria){
        return sysWechatUserMapper.toDto(sysWechatUserRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)));
    }

    @Override
    public SysWechatUserDto findById(Integer id) {
        SysWechatUser sysWechatUser = sysWechatUserRepository.findById(id).orElseGet(SysWechatUser::new);
        ValidationUtil.isNull(sysWechatUser.getId(),"SysWechatUser","id",id);
        return sysWechatUserMapper.toDto(sysWechatUser);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysWechatUserDto create(SysWechatUser resources) {
        return sysWechatUserMapper.toDto(sysWechatUserRepository.save(resources));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(SysWechatUser resources) {
        SysWechatUser sysWechatUser = sysWechatUserRepository.findById(resources.getId()).orElseGet(SysWechatUser::new);
        ValidationUtil.isNull( sysWechatUser.getId(),"SysWechatUser","id",resources.getId());
        sysWechatUser.copy(resources);
        sysWechatUserRepository.save(sysWechatUser);
    }

    @Override
    public void deleteAll(Integer[] ids) {
        for (Integer id : ids) {
            sysWechatUserRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<SysWechatUserDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (SysWechatUserDto sysWechatUser : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("UserId", sysWechatUser.getUserId());
            map.put("创建时间", sysWechatUser.getCreateTime());
            map.put("open_id", sysWechatUser.getOpenId());
            map.put("skey（自定义登录状态）", sysWechatUser.getSkey());
            map.put("最后登录时间", sysWechatUser.getLastVisitTime());
            map.put("session_key", sysWechatUser.getSessionKey());
            map.put("市", sysWechatUser.getCity());
            map.put("省", sysWechatUser.getProvince());
            map.put("国", sysWechatUser.getCountry());
            map.put("头像", sysWechatUser.getAvatarUrl());
            map.put("性别", sysWechatUser.getGender());
            map.put("网名", sysWechatUser.getNickName());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    /**
     * @Author 陈震洺
     * @Description 微信小程序登录
     * @Date 15:11 2020/5/11
     * @Param [weChatVo]
     * @return java.lang.String
     **/
    @Override
    public Map<String, Object> wxLogin(WeChatVo weChatVo, HttpServletRequest request) throws Exception {
        // 用户非敏感信息：rawData
        // 签名：signature
        JSONObject rawDataJson = JSON.parseObject(weChatVo.getRawData());
        // 1.接收小程序发送的code
        // 2.开发者服务器 登录凭证校验接口 appid + appsecret + code
        JSONObject SessionKeyOpenId = WechatUtil.getSessionKeyOrOpenId(systemConfig.getWx().getAppid(), systemConfig.getWx().getSecret(),weChatVo.getCode());
        // 3.接收微信接口服务 获取返回的参数
        String openid = SessionKeyOpenId.getString("openid");
        String sessionKey = SessionKeyOpenId.getString("session_key");

        // 4.校验签名 小程序发送的签名signature与服务器端生成的签名signature2 = sha1(rawData + sessionKey)
        String signature2 = DigestUtils.sha1Hex(weChatVo.getRawData() + sessionKey);//服务器端生成的签名
        String signature = weChatVo.getSignature();//小程序发送的签名
        if (!signature.equals(signature2)) {
            //    return GlobalResult.build(500, "签名校验失败", null);
            return null;
        }
        // 5.根据返回的User实体类，判断用户是否是新用户，是的话，将用户信息存到数据库；不是的话，更新最新登录时间

        SysWechatUser user = sysWechatUserRepository.findByOpenId(openid);

        UniqueOrderGenerate idWorker = new UniqueOrderGenerate(0, 0);
        long id = idWorker.nextId();
        String skey = String.valueOf(id);

        if (user == null) {
            // 用户信息入库
            String nickName = rawDataJson.getString("nickName");
            String avatarUrl = rawDataJson.getString("avatarUrl");
            String gender = rawDataJson.getString("gender");
            String city = rawDataJson.getString("city");
            String country = rawDataJson.getString("country");
            String province = rawDataJson.getString("province");

            user = new SysWechatUser();
            user.setOpenId(openid);
            user.setSkey(skey);
            user.setCreateTime(MyTime.getNowForTimestamp());
            user.setLastVisitTime(MyTime.getNowForTimestamp());
            user.setSessionKey(sessionKey);
            user.setCity(city);
            user.setProvince(province);
            user.setCountry(country);
            user.setAvatarUrl(avatarUrl);
            user.setGender(Integer.parseInt(gender));
            user.setNickName(nickName);

            sysWechatUserRepository.save(user);//保存信息
        } else {
            // 已存在，更新用户登录时间
            user.setLastVisitTime(MyTime.getNowForTimestamp());
            // 重新设置会话skey
            user.setSkey(skey);

            sysWechatUserRepository.save(user);
        }
        //encrypteData比rowData多了appid和openid
     //   JSONObject userInfo = WechatUtil.getUserInfo(weChatVo.getEncrypteData(), sessionKey, weChatVo.getIv());
        //6. 把新的skey返回给小程序

        Map<String, Object> weChatToken = getWeChatToken(openid, request);
        return weChatToken;
    }

    @Override
    public List<SysWechatUserDto> findByOpenId(WeChatVo weChatVo) {

        return null;
    }

    /**
     * @Author 陈震洺
     * @Description 根据openId获取用户信息
     * @Date 14:20 2020/5/12
     * @Param [openId]
     * @return me.zhengjie.modules.weChat.service.dto.UserWeChat
     **/
    @Override
    public UserWeChat findUserByOpenId(String openId) throws Exception {
         openId = "oLqXz5Ao0kkhmgTvYMyWV5YZThAM";
        Map<String, Object> params = Maps.newHashMap();
        params.put("openId",openId);
        List<UserWeChat> userInfoByOpenId = weChatDao.findUserInfoByOpenId(params);
        return userInfoByOpenId.get(0);
    }

    @Override
    public String checkBind(String code) {
        JSONObject SessionKeyOpenId = WechatUtil.getSessionKeyOrOpenId(systemConfig.getWx().getAppid(), systemConfig.getWx().getSecret(),code);
        // 3.接收微信接口服务 获取返回的参数
        String openid = SessionKeyOpenId.getString("openid");
        if (null == openid) {
            return null;
        }
        String token=wxService.getByOpenId(openid);
        System.out.println("获取的openid=="+openid);
        System.out.println("获取的token=="+token);
        return token;
    }

    public Map<String,Object> getWeChatToken(String openId,HttpServletRequest request) throws Exception {
        UserWeChat userByOpenId = this.findUserByOpenId(openId);
        // 生成令牌
        String token = tokenProvider.createWeChatToken(userByOpenId);
        // 保存在线信息
        onlineUserService.saveWXOnlineUser(userByOpenId.getUsername(), token, request);
        wxService.saveOpenIdToken(properties.getTokenStartWith() + token,openId);

        // 返回 token 与 用户信息
        Map<String,Object> authInfo = new HashMap<String,Object>(2){{
            put("token", properties.getTokenStartWith() + token);
            put("user",userByOpenId);
        }};
        return authInfo;
    }
}